// 工具类

import { crtChannel } from "./crtChannel";

const { ccclass, property } = cc._decorator;



@ccclass
export default class crtkc {

    static log(...logObj: any[]) {
        logObj.unshift('====>');
        console.log.apply(window, logObj);
    };
    static err(...logObj: any[]) {
        logObj.unshift('====>');
        console.error.apply(window, logObj);
    };

    static gameName: string = 'td_crt_v1';

    /** 替换removeAllChildren */
    static removeNodeChildren(node: cc.Node) {
        node.destroyAllChildren();
        // node.children.forEach((cl: cc.Node) => {
        //     if (cl.isValid) {
        //         cl.destroy();
        //     }
        // });
    }

    static buildBlocks(colCount: number, rowCount: number, itemWidth: number, parentNode: cc.Node, isFromBottom: boolean, itemNodeCreateFn: (x: number, y: number) => cc.Node, afterItemSetFn: (x: number, y: number, node: cc.Node) => void = null) {
        const shiftDistX = 0 - colCount + 1;
        const shiftDistY = (isFromBottom) ? 0 : 0 - rowCount + 1;
        // const shiftDistY = 0; //从底部开始显示
        const halfDistX = itemWidth / 2;
        const halfDistY = halfDistX;
        for (let x = 0; x < colCount; x++) {
            for (let y = 0; y < rowCount; y++) {
                const slotNode = itemNodeCreateFn(x, y);
                if (!slotNode) { //无则跳过
                    continue;
                }
                slotNode.parent = parentNode;
                slotNode.x = (shiftDistX + 2 * x) * halfDistX;
                slotNode.y = (shiftDistY + 2 * y) * halfDistY;
                if (afterItemSetFn) {
                    afterItemSetFn(x, y, slotNode);
                }
            }
        }
    }

    /** 从世界坐标获取节点在parent内的坐标 */
    static getPosFromWorldPos(target: cc.Node, parent: cc.Node, x: number = 0, y: number = 0) {
        return parent.convertToNodeSpaceAR(target.convertToWorldSpaceAR(cc.v2(x, y)));
    }

    /** 找到spine中指定bone的位置 */
    static findBonePos(spine: sp.Skeleton, slotName: string, node: cc.Node): cc.Vec2 {
        if (!slotName) {
            return null;
        }
        const bone = spine.findBone(slotName);
        if (!bone) {
            console.log('findBonePos未找到', slotName);
            return null;
        }
        const wps = node.convertToWorldSpaceAR(spine.node.getPosition());
        console.log('wps', wps.x, wps.y, 'bone', bone.worldX, bone.worldY, spine.node.x, spine.node.y);
        wps.x += bone.worldX;
        wps.y += bone.worldY;
        return wps;
    }

    static objToMap(saveMap: any): Map<string, number> {
        const out: Map<string, number> = new Map();
        crtkc.forMap(saveMap, (key: string) => {
            out.set(key, Number(saveMap[key]));
        });
        return out;
    }

    static mapToObj(map: Map<string, number>): any {
        const out = {};
        map.forEach((val: number, key: string) => {
            out[key] = val;
        });
        return out;
    }

    static str(inStr: string): string {
        if (inStr === undefined || inStr === null) {
            return '';
        }
        return inStr.trim();
    }

    static num(inStr: string): number {
        if (!inStr) {
            return 0;
        }
        return Number(inStr);
    }

    static strArr(inStr: string): Array<string> {
        if (!inStr) {
            return null;
        }
        inStr = inStr.replace(/\s/g, '').replace(/，/g, ',');
        return inStr.split(',');
    }
    static strNumArr(inStr: string): Array<number> {
        if (!inStr) {
            return null;
        }
        const out: Array<number> = [];
        // if (!inStr.replace) {
        //     crtkc.err('!!!!',inStr);
        // }
        inStr = inStr.replace(/\s/g, '').replace(/，/g, ',');
        const arr = inStr.split(',');
        for (let i = 0; i < arr.length; i++) {
            out.push(Number(arr[i] || 0));
        }
        return out;
    }

    static strSet(inStr: string): Set<string> {
        if (!inStr) {
            return null;
        }
        const arr = crtkc.strArr(inStr);
        const out: Set<string> = new Set();
        for (let i = 0; i < arr.length; i++) {
            out.add(arr[i]);
        }
        return out;
    }

    /** 解析属性用,如果值包含%号则为百分比,Map的值>1为数值,<1为百分比，如: P1:20,P3:10% */
    static strMap(inStr: string): Map<string, number> {
        if (!inStr) {
            return null;
        }
        const arr = crtkc.strArr(inStr);
        const out: Map<string, number> = new Map();
        for (let i = 0; i < arr.length; i++) {
            const one = arr[i];
            const a2 = one.split(':');
            if (a2.length !== 2) {
                throw new Error('解析错误!' + inStr);
            }
            // const isPercent = (a2[1].indexOf('%') > 0);
            a2[1].replace(/%/g, '');
            let val = parseInt(a2[1]);
            if (isNaN(val)) {
                throw new Error('解析错误!' + a2 + ',' + inStr);
            }
            // if (isPercent) {
            //     val = val / 100;
            // }
            out.set(a2[0], val);
        }
        return out;
    }

    /**解析字符串 */
    static strSplit(str: string) {
        const input = str;
        const result = input.match(/[a-zA-Z_]+|\d+/g);
        let letters = null;
        let numbers = null;
        if (result) {
            letters = result.filter(item => isNaN(Number(item))).toString();
            numbers = result.filter(item => !isNaN(Number(item))).toString();
        }
        return [letters, parseInt(numbers)];
    }
    /**
    * 抖动目标节点
    * @param node 目标节点
    * @param durating 动画持续时间
    * @param ang 转动角度
    * @param times 重复次数
    * @param isforever 是否永远动画（true：times无效）
    */
    public static ani_JitterNode(node: cc.Node, duration: number, ang: number, times: number, isforever: boolean = false) {
        if (isforever) {
            let ani1 = cc.tween(node).by(duration / 4, { angle: ang }).by(duration / 2, { angle: -ang * 2 }).by(duration / 4, { angle: ang });
            let ani2 = ani1.delay(0.5);
            cc.tween(node).repeatForever(ani2).start();
        } else {
            let ani1 = cc.tween(node).by(duration / 2, { angle: ang }).by(duration / 2, { angle: -ang })
            let ani2 = cc.tween(node).sequence(ani1, cc.tween(node).delay(0.1), ani1.reverseTime()).delay(0.1);
            ani2.repeat(times).start();
        }
    }

    /**是否为pad */
    static isPad() {
        //屏幕比小于16:9被当作IPad
        let isPad = (cc.winSize.height / cc.winSize.width) < (16 / 9);
        return isPad;
    }

    // private static toastNode: cc.Node = null;
    // // private static isToastShow: boolean = false;
    // static toast(parentNode: cc.Node, msg: string) {
    //     // if (crtkc.isToastShow) {
    //     //     return;
    //     // }
    //     // crtkc.isToastShow = true;
    //     // crtkc.log('toast 1');
    //     if (!crtkc.toastNode) {
    //         crtkc.toastNode = cc.instantiate(crtResMgr.instance().getPrefab('toast'));
    //         // crtkc.log('toast 2');
    //     } else if (crtkc.toastNode.active) {
    //         // crtkc.log('toast 3');
    //         return;
    //     }
    //     // crtkc.log('toast 4');
    //     crtkc.toastNode.getComponent(crtToast).setTxt(msg);
    //     crtkc.toastNode.parent = parentNode;
    //     crtkc.toastNode.active = true;
    //     crtkc.toastNode.opacity = 255;
    //     crtkc.toastNode.setPosition(0, -50);
    //     // crtkc.log('toast 5');
    //     cc.tween(crtkc.toastNode).to(0.4, { y: 50 }).to(0.5, { y: 50 }).call(() => {
    //         crtkc.toastNode.active = false;
    //         // crtkc.log('toast 6');
    //         // crtkc.isToastShow = false;
    //     }).start();
    // }

    /**
     * 快速迭代一个object,注意call方法可通过返回非undefined的值中断循环并返回这个值
     * @param map 
     * @param call 返回undefined则继续循环,否则循环中断并返回此值
     * @returns 
     */
    static forMap(map: any, call: (key?: string, i?: number) => any): any {
        const keys: string[] = Object.keys(map);
        for (let i = 0, len = keys.length; i < len; i++) {
            const re = call(keys[i], i);
            if (re !== undefined) {
                return re;
            }
        }
        return null;
    }

    /**
     * 加载远程子游戏
     * @param url 
     * @param gameKey 
     * @param ver 
     * @param onProcess 
     * @param onComplete 
     */
    static loadSubGame(url: string, gameKey: string, ver: string, onProcess?: (finish: number, total: number) => void, onComplete?: (err: any, prefab?: cc.Prefab, bundle?: cc.AssetManager.Bundle) => void) {
        // cc.assetManager.loadBundle(url,
        cc.assetManager.loadAny({ url },
            {
                version: ver,
                ext: 'bundle',
                preset: 'bundle',
                __isNative__: true,
            },
            (n, t) => {
                // kc.log('加载子游戏', n, t); //这里仅加载bundle，process很快不需要处理, 真正要处理process的是后面bundle的load
            },
            // (err, bundle) => {
            (err, data) => {
                if (err) {
                    onComplete(err);
                    return;
                }
                const bundle = new cc.AssetManager.Bundle();
                data.base = data.base || url + '/';
                bundle.init(data);
                bundle.load('pkg/' + gameKey, cc.Prefab, onProcess, (err, asset: cc.Prefab) => {
                    if (err) {
                        onComplete(err);
                        return;
                    }
                    onComplete(null, asset, bundle);
                });
            });
    }


    /**
      * 不使用promise循环调用异步方法,使用数组作为入参,批量调用带callback(err,data)的异步方法,data为数组长度的返回值组.
      * 注意方法的执行是并行的,无顺序.
      * 使用例子:
      const t1 = function(inStr, callback) {
          if (inStr === 'a') {
              return callback('ERR-a');
          }
          const out = 't1-' + inStr;
          callback(null, out);
      };
      callbackArr(['aaa','a1', 'bbb'], t1, function(e, re) {
          if (e) {
              console.error(e, re);
              return;
          }
          console.log('re:%j', re);
      });
   * @param arrInput 
   * @param fn 
   * @param callback 
   * @param isCallForEach 是否每次执行都callback,以便处理process进度，默认为false
   */
    static callbackArr = function (arrInput, fn, callback, isCallForEach = false) {
        const fArr = [];
        const len = arrInput.length;
        const callbackAll = (err, callObj) => {
            if (err) {
                return callback(err, fArr);
            }
            if (callObj === undefined) {
                callObj = null;
            }
            fArr.push(callObj);
            if (isCallForEach || fArr.length === len) {
                return callback(null, fArr);
            }
        };
        for (let i = 0; i < len; i++) {
            const inputOne = arrInput[i];
            fn(inputOne, callbackAll);
        }
    };

    //viewSize
    //-----------------
    // public static view_x: number = 0;
    // public static view_y: number = 0;
    // public static view_mag: number = 0;
    static init() {
        crtkc.loadData(null);
        // crtkc.initView();
    }

    // static initView(){
    //     const viewSize = cc.view.getVisibleSize();
    //     crtkc.view_x = viewSize.width / 2;
    //     crtkc.view_y = viewSize.height / 2;
    //     crtkc.view_mag = cc.v2(crtkc.view_x, crtkc.view_y).mag();
    //     crtkc.log('可视区域', crtkc.view_x, crtkc.view_y);
    //     cc.view.setResizeCallback(crtkc.initView);
    // }

    static leftPad(str: string, len: number, padStr: string = '0') {
        const padLen = len - str.length;
        if (padLen <= 0) {
            return str;
        }
        for (let i = 0; i < padLen; i++) {
            str = padStr + str;
        }
        return str;
    }
    /**
     * 计算从当前时间到当天 24:00 剩余的秒数
     * @returns 剩余秒数
     */
    public static getRemainingSeconds(): number {
        const now = new Date(); // 当前时间
        const midnight = new Date(now);

        // 设置为当天 24:00
        midnight.setHours(24, 0, 0, 0);

        // 计算剩余的毫秒数并转换为秒
        const diffInMs = midnight.getTime() - now.getTime();
        return Math.floor(diffInMs / 1000);
    }

    /**
     * 将秒数转换为 00:00 或 00:00:00 格式的时间字符串
     * @param seconds 剩余的总秒数
     * @param showHours 是否显示小时数，默认为 true
     * @returns 格式化后的时间字符串
     */
    public static format(seconds: number, showHours: boolean = true): string {
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const remainingSeconds = seconds % 60;

        if (showHours) {
            // 格式为 HH:MM:SS
            return `${this.formatTime(hours)}:${this.formatTime(minutes)}:${this.formatTime(remainingSeconds)}`;
        } else {
            // 格式为 MM:SS
            return `${this.formatTime(minutes)}:${this.formatTime(remainingSeconds)}`;
        }
    }

    /**
     * 格式化数字为两位数
     * @param num 需要格式化的数字
     * @returns 格式化后的字符串
     */
    private static formatTime(num: number): string {
        return num < 10 ? `0${num}` : `${num}`;
    }
    rndInt(min: number, max: number) {
        return Math.round(min + Math.floor(Math.random() * (max - min + 1)));
    }


    /** 显示时间间隔 */
    static showPassTime(passMs: number): string {
        let passSec = Math.floor(passMs / 1000); //换成秒
        const day = Math.floor(passSec / 86400);
        if (day > 0) {
            passSec = passSec % 86400;
        }
        const hour = Math.floor(passSec / 3600);
        if (hour > 0) {
            passSec = passSec % 3600;
        }
        const min = '' + Math.floor(passSec / 60);
        const sec = '' + passSec % 60;
        const dayStr = day > 0 ? day + '天' : '';
        return dayStr + this.leftPad('' + hour, 2) + ':' + this.leftPad(min, 2) + ':' + this.leftPad(sec, 2);
    }

    /**计算中间相隔了多少天 */
    static calculateDays(timestamp1: number, timestamp2: number): number {
        const date1 = new Date(timestamp1);
        const date2 = new Date(timestamp2);
        const timeDiff = Math.abs(date2.getTime() - date1.getTime());
        const diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
        return diffDays;
    }



    static showPercent(decimal: number): string {
        return (decimal * 100) + '%';
    }

    static bezier(C1: number, C2: number, C3: number, C4: number, t: number) {
        var t1 = 1 - t;
        return t1 * (t1 * (C1 + (C2 * 3 - C1) * t) + C3 * 3 * t * t) + C4 * t * t * t;
    }
    /**
     * 带转向的bezierTo,其node的angle会随之变化
     * @param itemNode 
     * @param duration 
     * @param p1 
     * @param p2 
     * @param to 
     * @param opts 
     * @returns tween对象，可直接在后接play等
     */
    static bezierTo(itemNode: cc.Node, duration: number, p1: cc.Vec2, p2: cc.Vec2, to: cc.Vec3, opts?: any): cc.Tween {
        if (!opts) {
            opts = Object.create(null);
        }
        opts.progress = function (start, end, current, t) {
            const newX = crtkc.bezier(start.x, p1.x, p2.x, end.x, t);
            const newY = crtkc.bezier(start.y, p1.y, p2.y, end.y, t);
            itemNode.angle = crtkc.posToAngle(current.x, current.y, newX, newY);
            current.x = newX;
            current.y = newY;
            return current;
        }
        return cc.tween(itemNode).to(duration, { position: to }, opts);
    }


    /** Math.PI / 180,弧度角度转换时使用 */
    static PI180a: number = Math.PI / 180;

    /** 180 / Math.PI,弧度角度转换时使用 */
    static PI180b: number = 180 / Math.PI;

    /**
     * x轴方向的向量转角度,用于向node.angle赋值
     * @param orgX 
     * @param orgY 
     * @param targetX 
     * @param targetY 
     * @returns 
     */
    static posToAngle(orgX: number, orgY: number, targetX: number, targetY: number) {
        return crtkc.posToRadian(orgX, orgY, targetX, targetY) * crtkc.PI180b;
    }

    /**
     * x轴方向的向量旋转弧度,用于向量的rotation操作
     * @param orgX 
     * @param orgY 
     * @param targetX 
     * @param targetY 
     * @returns 
     */
    static posToRadian(orgX: number, orgY: number, targetX: number, targetY: number) {
        return Math.atan2(targetY - orgY, targetX - orgX);
    }


    /**
     * deepClone对象,但不包括prototype，与Object.assign不同，其属性的引用也会clone，支持null,Array,Date,RegExp,Error等各种类型
     * @param  {Ojbect} json
     * @return {Object}
     */
    static clone(json) {
        if (Array.isArray(json)) {
            const target = [];
            for (let i = 0, len = json.length; i < len; i++) {
                target.push(crtkc.clone(json[i]));
            }
            return target;
        }
        if (Object.prototype.toString.call(json) === '[object Object]') {
            const target = {};
            crtkc.forMap(json, (key) => {
                target[key] = crtkc.clone(json[key]);
            });
            return target;
        }
        return json;
    };

    /**
     * 和当前时间相比，是否为新的一天
     * @param timeA 毫秒数
     * @returns 
     */
    static isNewDay(timeA: number): boolean {
        if (timeA <= 0) {
            return true;
        }
        const d1 = new Date(timeA);
        const d2 = new Date();
        if (d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate()) {
            return false;
        }
        return true;
    }

    static getRndItem(e: any) {
        const i: any = {
            i: e.i,
            id: e.id,
            n: e.n,
        };
        if (Array.isArray(i.i)) {
            i.i = crtkc.rndOne(i.i);
        }

        if (Array.isArray(i.id)) {
            i.id = crtkc.rndOne(i.id);
        }

        if (Array.isArray(i.n)) {
            i.n = crtkc.rndOne(i.n).v;
        }

        return i;
    }

    static rndOne(e) {
        return e[crtkc.rndInt(0, e.length - 1)]
    }
    static rndInt(e, t) {
        return Math.round(e + Math.floor(Math.random() * (t - e + 1)))
    }

    // /**
    //  * layout布局,要求fatherNode为空(否则无法计算更新时其中的数目)
    //  * @param uiConf 
    //  * @param fatherNodeArr 
    //  * @param paraFn 
    //  * @param itemDataArr 
    //  * @param caller 
    //  * @param isUpdate 
    //  * @returns 
    //  */
    // static uiLayout(uiConf: any, fatherNodeArr: Array<cc.Node>, paraFn: (itemNode: cc.Node, i?: number, oneItemData?: any, oneLayout?: any, isUpdate?: boolean) => void, itemDataArr: Array<any>, caller: any, isUpdate: boolean = false) {
    //     if (!fatherNodeArr || !itemDataArr || itemDataArr.length !== fatherNodeArr.length) {
    //         crtkc.err('[ERR]uiLayout的fatherNodeArr或itemDataArr配置错误');
    //         return;
    //     }
    //     const layoutArr = uiConf.layout;
    //     const getItem = (itemPrefab): cc.Node => {
    //         if (typeof itemPrefab === 'function') {
    //             return itemPrefab();
    //         }
    //         return cc.instantiate(crtResMgr.instance().getPrefab(itemPrefab));
    //     }
    //     for (let i = 0; i < layoutArr.length; i++) {
    //         const oneLayout = layoutArr[i];
    //         const itemData = itemDataArr[i];
    //         const fatherNode = fatherNodeArr[i];
    //         const itemDataLen = itemData.length;
    //         if (oneLayout.type === 'gird') { //网格型
    //             const colCount = oneLayout.colCount || itemDataLen;
    //             const itemCount = (itemDataLen > (oneLayout.minItems || 0)) ? itemDataLen : (oneLayout.minItems || 0);
    //             const rowCount = Math.ceil(itemCount / colCount) || 1;
    //             const oldLen = (isUpdate) ? fatherNode.children.length : 0;
    //             const itemNode = getItem(oneLayout.itemPrefab);
    //             const itemPadding = oneLayout.itemPadding || [0, 0];
    //             const allPadding = oneLayout.padding || [0, 0, 0, 0];
    //             const halfDistX = itemNode.width / 2 + itemPadding[0];
    //             const halfDistY = itemNode.height / 2 + itemPadding[1];
    //             const shiftDistX = 0 - colCount + 1;
    //             const shiftDistY = 0 - rowCount + 1;
    //             let col: number = 0;
    //             let row: number = 0;
    //             for (let k = 0; k < itemCount; k++) {
    //                 if (col >= colCount) {
    //                     col = 0;
    //                     row++;
    //                 }
    //                 let thisItemNode: cc.Node;
    //                 if (isUpdate && k < oldLen) {
    //                     thisItemNode = fatherNode.children[k];
    //                 } else {
    //                     thisItemNode = getItem(oneLayout.itemPrefab);
    //                 }
    //                 const item = (k < itemDataLen) ? itemData[k] : null;
    //                 if (paraFn.call(caller, thisItemNode, k, item, oneLayout, isUpdate)) {
    //                     col++; //空一个位置
    //                     continue;
    //                 }
    //                 thisItemNode.parent = fatherNode;
    //                 thisItemNode.x = (shiftDistX + 2 * col) * halfDistX - allPadding[1] + allPadding[3];
    //                 thisItemNode.y = (shiftDistY + 2 * row) * halfDistY * -1 - allPadding[0] + allPadding[2];
    //                 if (oneLayout.itemProps) {
    //                     crtkc.forMap(oneLayout.itemProps, (key) => {
    //                         thisItemNode[key] = oneLayout.itemProps[key];
    //                     });
    //                 }
    //                 col++;
    //                 // kc.log('thisItemNode X',thisItemNode.x,'Y',thisItemNode.y,'H',(shiftDistX + 2 * j));
    //             }
    //             if (isUpdate && oldLen > itemCount) {
    //                 //如果更新时item变少，删除多余item
    //                 for (let l = itemCount; l < oldLen; l++) {
    //                     fatherNode.removeChild(fatherNode.children[itemCount]);
    //                 }
    //             }
    //         } else if (oneLayout.type === 'gird1') { //网格1型:从上向下,从左向右
    //             const rowCount = oneLayout.rowCount || itemDataLen;
    //             const itemCount = (itemDataLen > (oneLayout.minItems || 0)) ? itemDataLen : (oneLayout.minItems || 0);
    //             const colCount = Math.ceil(itemCount / rowCount) || 1;
    //             const oldLen = (isUpdate) ? fatherNode.children.length : 0;
    //             const itemNode = getItem(oneLayout.itemPrefab);
    //             const itemPadding = oneLayout.itemPadding || [0, 0];
    //             const allPadding = oneLayout.padding || [0, 0, 0, 0];
    //             const halfDistX = itemNode.width / 2 + itemPadding[0];
    //             const halfDistY = itemNode.height / 2 + itemPadding[1];
    //             const shiftDistX = 0 - colCount + 1;
    //             const shiftDistY = 0;
    //             let row: number = 0;
    //             let col: number = 0;
    //             for (let k = 0; k < itemCount; k++) {
    //                 if (row >= rowCount) {
    //                     row = 0;
    //                     col++;
    //                 }
    //                 let thisItemNode: cc.Node;
    //                 if (isUpdate && k < oldLen) {
    //                     thisItemNode = fatherNode.children[k];
    //                 } else {
    //                     thisItemNode = getItem(oneLayout.itemPrefab);
    //                 }
    //                 const item = (k < itemDataLen) ? itemData[k] : null;
    //                 paraFn.call(caller, thisItemNode, k, item, oneLayout, isUpdate);
    //                 thisItemNode.parent = fatherNode;
    //                 thisItemNode.x = (shiftDistX + 2 * col) * halfDistX - allPadding[1] + allPadding[3];
    //                 thisItemNode.y = (shiftDistY + 2 * row) * halfDistY * -1 - allPadding[0] + allPadding[2];
    //                 if (oneLayout.itemProps) {
    //                     crtkc.forMap(oneLayout.itemProps, (key) => {
    //                         thisItemNode[key] = oneLayout.itemProps[key];
    //                     });
    //                 }
    //                 row++;
    //                 // kc.log('thisItemNode X',thisItemNode.x,'Y',thisItemNode.y,'H',(shiftDistX + 2 * j));
    //             }
    //             if (isUpdate && oldLen > itemCount) {
    //                 //如果更新时item变少，删除多余item
    //                 for (let l = itemCount; l < oldLen; l++) {
    //                     fatherNode.removeChild(fatherNode.children[itemCount]);
    //                 }
    //             }
    //         } else if (oneLayout.type === 'rect') { //中空矩形 ，将保持一个完整矩形，item数量是固定的，首次布置将填满，且之后不再有变化
    //             if (!oneLayout.rowCount || !oneLayout.colCount || oneLayout.rowCount < 2 || oneLayout.colCount < 2) {
    //                 crtkc.err('uiLayout rect配置错误, colCount or rowCount');
    //                 return;
    //             }
    //             const itemNode = getItem(oneLayout.itemPrefab);
    //             const itemPadding = oneLayout.itemPadding || [0, 0];
    //             const allPadding = oneLayout.padding || [0, 0, 0, 0];
    //             const halfDistX = itemNode.width / 2 + itemPadding[0];
    //             const halfDistY = itemNode.height / 2 + itemPadding[1];
    //             const shiftDistX = 0 - oneLayout.colCount + 1;
    //             const shiftDistY = 0 - oneLayout.rowCount + 1;
    //             let col: number = 0;
    //             let row: number = 0;
    //             //按上右下左顺时针画线的方式实现
    //             let step: number = 0; //0为上，1为右，2为下，3为左
    //             let stepStop: number = oneLayout.colCount;
    //             let itemId: number = 0;
    //             for (let i = 0; i < stepStop; i++) {
    //                 const item = itemData[itemId];
    //                 let theItemNode: cc.Node = getItem(oneLayout.itemPrefab);
    //                 if (isUpdate) {
    //                     theItemNode = fatherNode.children[itemId];
    //                 }
    //                 paraFn.call(caller, theItemNode, itemId, item, oneLayout, isUpdate);
    //                 theItemNode.parent = fatherNode;
    //                 let nextStop = 0;
    //                 if (step === 0) {
    //                     //上
    //                     col = i;
    //                     row = 0;
    //                     nextStop = oneLayout.rowCount;
    //                 } else if (step === 1) {
    //                     //右
    //                     col = oneLayout.colCount - 1;
    //                     row = i;
    //                     nextStop = oneLayout.colCount;
    //                 } else if (step === 2) {
    //                     //下
    //                     col = oneLayout.colCount - i - 1;
    //                     row = oneLayout.rowCount - 1;
    //                     nextStop = oneLayout.rowCount - 1;
    //                 } else if (step === 3) {
    //                     //左
    //                     col = 0;
    //                     row = oneLayout.rowCount - 1 - i;
    //                     nextStop = -1;
    //                 }
    //                 // crtkc.log('i', i, 'step', step, 'col', col, 'row', row, 'itemId',itemId);
    //                 theItemNode.x = (shiftDistX + 2 * col) * halfDistX - allPadding[1] + allPadding[3];
    //                 theItemNode.y = (shiftDistY + 2 * row) * halfDistY * -1 - allPadding[0] + allPadding[2];
    //                 // crtkc.log('theItemNode x:', theItemNode.x, ',y:', theItemNode.y, itemId,theItemNode.opacity);
    //                 if (i + 1 === stepStop) {
    //                     if (nextStop < 0) {
    //                         break;
    //                     }
    //                     step++;
    //                     i = 0; //新一边开始
    //                     stepStop = nextStop;
    //                 }
    //                 itemId++;
    //             }
    //         } else if (oneLayout.type === 'posArr') {
    //             if (!oneLayout.posArr) {
    //                 crtkc.err('uiLayout posArr配置错误, posArr');
    //                 return;
    //             }
    //             for (let i = 0; i < itemDataLen; i++) {
    //                 const item = itemData[i];
    //                 let theItemNode: cc.Node = getItem(oneLayout.itemPrefab);
    //                 if (isUpdate) {
    //                     theItemNode = fatherNode.children[i];
    //                 }
    //                 paraFn.call(caller, theItemNode, i, item, oneLayout, isUpdate);
    //                 theItemNode.parent = fatherNode;
    //                 theItemNode.setPosition(oneLayout.posArr[i]);
    //             }
    //         } else if (oneLayout.type === 'circle') {
    //             if (!oneLayout.radius) {
    //                 crtkc.err('uiLayout circle配置错误, radius');
    //                 return;
    //             }
    //             const vect = cc.v2(oneLayout.radius, 0);
    //             const angleShift = 2 * Math.PI / itemDataLen;
    //             for (let i = 0; i < itemDataLen; i++) {
    //                 vect.rotateSelf(i * angleShift);
    //                 const item = itemData[i];
    //                 let theItemNode: cc.Node = getItem(oneLayout.itemPrefab);
    //                 if (isUpdate) {
    //                     theItemNode = fatherNode.children[i];
    //                 }
    //                 paraFn.call(caller, theItemNode, i, item, oneLayout, isUpdate);
    //                 theItemNode.parent = fatherNode;
    //                 theItemNode.setPosition(vect);
    //             }
    //         }
    //     }
    // }

    //---------------------------------

    static randomInt(lowerValue: number, upperValue: number): number {
        return Math.floor(Math.random() * (upperValue - lowerValue + 1) + lowerValue);
    };

    static randomBool(randomRate: number = 0.5): boolean {
        return Math.random() < randomRate;
    }

    static randomPosNeg1(): number {
        return (Math.random() > 0.5) ? 1 : -1;
    }

    /**
     * create a random string
     * @param  {int} len     需要的随机string长度
     * @param  {char string} charArr char范围
     * @return {string}
     */
    static randomStr(len: number, charArr?: string): string {
        const charTable = charArr || 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        let out = '';
        for (let i = 0; i < len; i++) {
            out += charTable[crtkc.randomInt(0, len - 1)];
        }
        return out;
    }
    /**
     * 数组去重
     * @param a 
     * @param b 可选
     * @returns 去重后的新数组
     */
    static distinctArr(a: Array<any>, b: Array<any> = []): Array<any> {
        // const arr = a.concat(b);
        // const result = [];
        // const obj = {};
        // for (const val of arr) {
        //     if (!obj[val]) { //当数组元素为数字时,obj[i]可能有问题
        //         result.push(val);
        //         obj[val] = 1;
        //     }
        // }
        // return result;
        return Array.from(new Set([...a, ...b]));
    }

    /**
     * 数组相减，返回新数组
     * @param a 被减数组
     * @param b 需要减掉的数组
     * @returns 返回不包含b的新数组
     */
    static arrSub(a: Array<any>, b: Array<any>): Array<any> {
        const aSet = new Set(b);
        const result = [];
        for (let i = 0, len = a.length; i < len; i++) {
            const val = a[i];
            if (!aSet.has(val)) {
                result.push(val);
            }
        }
        return result;
    }

    /**
     * 将Array重新洗牌,直接更新原数组
     * @param  {array} arr
     * @return {array} 返回洗过的原数组
     */
    static shuffle = function (arr) {
        let m = arr.length;
        let i = 0;
        while (m) {
            i = (Math.random() * m--) >>> 0;
            [arr[m], arr[i]] = [arr[i], arr[m]];
        }
        return arr;
    };


    /**
    * 从一个数组中随机取得其中一个元素
    * @param targetArray 随机数组
    */
    static getRandomValueFromArray(targetArray: Array<any>): any {
        if (targetArray == null || targetArray.length <= 0) {
            return null;
        }
        let length = targetArray.length;
        let random = Math.random();
        let index = Math.floor(random * length);
        return targetArray[index];
    }


    /**
     * 从数组中随机取n个,仅返回index
     * @param arr 
     * @param num 
     * @returns 
     */
    static randomChooseIndex(arr: Array<any>, num: number): Array<number> {
        const out = new Array<number>();
        const iArr = new Array<number>();
        const sum = arr.length;
        for (let i = 0; i < sum; i++) {
            iArr[i] = i;
        }
        for (let j = 0; j < num; j++) {
            const index = crtkc.randomInt(0, iArr.length - 1);
            out.push(iArr.splice(index, 1)[0]);
        }
        return out;
    }

    /**
     * 从数组中随机取n个,不影响原数组
     * @param  {array} arr
     * @param  {int} num
     * @return {array}
     */
    static randomChoose(arr: Array<any>, num: number): Array<any> {
        if (!num || num === 1) {
            return [arr[Math.floor((Math.random() * arr.length))]];
        }
        //方法1
        // const shuffledArr = shuffle(arr.slice(0));
        // return shuffledArr.slice(0, num);
        //方法2
        const shuffled = arr.slice(0);
        if (num > arr.length) {
            return shuffled;
        }
        let temp = 0;
        let index = 0;
        let i = arr.length;
        const min = i - num;
        while (i-- > min) {
            index = Math.floor((i + 1) * Math.random());
            temp = shuffled[index];
            shuffled[index] = shuffled[i];
            shuffled[i] = temp;
        }
        return shuffled.slice(min);
    };


    /**
     * 按概率配置抽卡: randomDraw({'item1':0.1,'item2':0.2,'item3':0.7})
     * @param {probabilityConf} 如: {'item1':0.1,'item2':0.2,'item3':0.7}
     */
    static randomDraw(probabilityConf: any): string {
        const r = Math.random();
        let areaLimit = 0;
        return crtkc.forMap(probabilityConf, (key) => {
            if (r > areaLimit && r < areaLimit + probabilityConf[key]) {
                return key;
            }
            areaLimit += probabilityConf[key];
        });
    };
    /**
     * 从数组中随机提取出n个,注意原数组会减少n个,效率比randomChoose略高
     * @param  {array} arr
     * @param  {int} num
     * @return {array}
     */
    static randomPick(arr: Array<any>, num: number): Array<any> {
        if (!num || num === 1) {
            return arr.splice(Math.floor((Math.random() * arr.length)), 1);
        }
        if (num > arr.length) {
            return arr;
        }
        const out = [];
        for (let i = 0; i < num; i++) {
            const index = Math.floor((Math.random() * arr.length));
            out.push(arr.splice(index, 1)[0]);
        }
        return out;
    };

    /**战车源码中算法,获取idx */
    public static rollWithPower(e) {
        var t = 0;
        for (var i in e) {
            t += +e[i];
        }
        var a = Math.random() * t;
        // cc.log("ab\u6d4b\u8bd5 rollWithPower rand power", t, a);
        var n, o = 0;
        for (var i in e) {
            if (a <= (o += +e[i])) {
                n = i;
                break;
            }
        }
        return n;
    }

    static norm(e, t) {
        return Math.sqrt(e * e + t * t)
    }
    static clamp(e, t, i) {
        return e < t ? t : e > i ? i : e
    }


    //动作效果
    //-----------------
    //在使用shader时重新定义正确的rect,以支持合图
    static resetTexRect(sprite: cc.Sprite) {
        if (!sprite.spriteFrame) {
            crtkc.err('[ERR]resetTexRect', sprite);
            return;
        }
        const rect = sprite.spriteFrame.getRect();
        const texture = sprite.spriteFrame.getTexture();
        const r1 = rect.x / texture.width;
        const r2 = rect.y / texture.height;
        const r3 = (rect.x + rect.width) / texture.width;
        const r4 = (rect.y + rect.height) / texture.height;
        const newRect: cc.Vec4 = new cc.Vec4(r1, r2, r3 - r1, r4 - r2);
        sprite.getMaterial(0).setProperty('_TexRect', newRect);
    }

    //抖动
    static shake(theNode: cc.Node, shakeCount: number = 3, wave: number = 0, gap: number = 10): any {
        const shakeObj = {
            'shakeCount': shakeCount,
            'node': theNode,
            'orgX': theNode.x,
            'orgY': theNode.y,
            'isDone': false,
            'doneFn': () => { },
            'done': null,
            'tw': null,
        };
        //结束回调,第2个参数为caller
        shakeObj.done = (doneCallback?: any, caller?: any) => {
            if (doneCallback) {
                shakeObj.doneFn = () => {
                    // console.log('shake done', shakeObj.node.uuid);
                    // cc.Tween.stopAllByTarget(shakeObj.node); //在done回调中决定是否stopAllByTarget
                    doneCallback.call(caller || shakeObj.node);
                };
            }
            return shakeObj;
        };
        shakeObj.tw = cc.tween(shakeObj.node)
            .repeat(shakeObj.shakeCount, cc.tween()
                .to(0.05, { x: shakeObj.orgX + gap, y: shakeObj.orgY + wave })
                .to(0.05, { x: shakeObj.orgX - gap, y: shakeObj.orgY - wave }))
            .call(() => {
                shakeObj.doneFn();
            });
        shakeObj.tw.start();
        return shakeObj;
    }




    //msg相关
    //-----------------
    private static msgMap = {};
    static resetMsgReciever(key: string, handleMsg: (msg?: any) => boolean): void {
        crtkc.msgMap[key] = handleMsg;
    }
    static delMsgReciever(key: string) {
        delete crtkc.msgMap[key];
    }
    /**
     * 注册事件接收器,返回true可继续执行下个事件,false则中断后续事件
     * @param key 
     * @param handleMsg 
     * @returns 
     */
    static regMsgReciever(key: string, handleMsg: (msg?: any) => boolean): void {
        const oldHandleMsg = crtkc.msgMap[key];
        if (!oldHandleMsg) {
            crtkc.resetMsgReciever(key, handleMsg);
            return;
        }
        crtkc.msgMap[key] = (msg) => {
            if (oldHandleMsg(msg)) {
                return handleMsg(msg);
            }
            return false;
        };
    }
    static sendMsg(key: string, msg?: any) {
        const handleMsg = crtkc.msgMap[key];
        if (handleMsg) {
            handleMsg(msg);
        }
    }
    static isSameDay(time1: number, time2: number): boolean {
        if (time1 == null || time2 == null) {
            return false;
        }
        if (new Date(time1).toDateString() === new Date(time2).toDateString()) {
            //同一天
            return true
        }
        return false;
    }

    static second2date(e) {
        e = Math.round(e);
        var t = Math.floor(e / 60);
        if (t > 60) {
            var i = Math.floor(t / 60);
            return e -= 60 * t,
                i + ":" + ((t -= 60 * i) < 10 ? "0" + t : t) + ":" + (e < 10 ? "0" + e : e)
        }
        return (t < 10 ? "0" + t : t) + ":" + ((e -= 60 * t) < 10 ? "0" + e : e)
    }

    //-----------------
    //node向某个对象移动一段距离
    // static moveTo(moveNode: cc.Node, targetPo: cc.Vec2, moveDist: number, moveTime: number = 0.5, callback: () => void = function () { }) {
    //     const distPo: cc.Vec2 = targetPo.sub(cc.v2(moveNode.position));
    //     const dist: number = distPo.mag();
    //     if (dist === 0) {
    //         callback();
    //         return;
    //     }
    //     const mX: number = distPo.x / dist * moveDist;
    //     const mY: number = distPo.y / dist * moveDist;
    //     cc.tween(moveNode).to(moveTime, { x: moveNode.x + mX, y: moveNode.y + mY }).call(callback).start();
    // }

    // /**
    //  * 向某个方向移动，用于update()
    //  * @param moveNode 
    //  * @param targetPo 
    //  * @param fromPo 
    //  * @param moveSpeed 需要与 dt 相乘
    //  * @param scaleX 是否转向,为0则无操作
    //  * @param beforeMove 存在且返回false则停止移动
    //  * @returns 
    //  */
    // static updateMove(moveNode: cc.Node, targetPo: cc.Vec2, fromPo: cc.Vec2, moveSpeed: number = 10, scaleX: number = 0, beforeMove?: (distPo?: cc.Vec2, dist?: number, mX?: number, mY?: number) => boolean): void {
    //     if (!moveNode || moveSpeed === 0) {
    //         return;
    //     }
    //     const distPo: cc.Vec2 = targetPo.sub(fromPo);
    //     const dist: number = distPo.mag();
    //     if (dist < 10) {
    //         return;
    //     }
    //     //是否转向,为0则无操作
    //     if (scaleX !== 0) {
    //         if ((distPo.x < 0 && moveNode.scaleX > 0) || (distPo.x > 0 && moveNode.scaleX < 0)) {
    //             moveNode.scaleX = -moveNode.scaleX;
    //         }
    //     }
    //     const mX: number = distPo.x / dist;
    //     const mY: number = distPo.y / dist;
    //     if (beforeMove) {
    //         if (!beforeMove(distPo, dist, mX, mY)) {
    //             return;
    //         }
    //     }
    //     moveNode.x += mX * moveSpeed;
    //     moveNode.y += mY * moveSpeed;
    //     // moveNode.setPosition(cc.v2(moveNode).lerp(targetPo, 0.5));
    // }


    static twoInt(int: number) {
        return (int < 10) ? '0' + int : int;
    };

    static todayStr(): string {
        const d = new Date();
        const re = '' + d.getFullYear() + crtkc.twoInt(d.getMonth() + 1) + crtkc.twoInt(d.getDate());
        return re;
    };

    private static saveObj = {};

    static isDataChange: boolean = false;
    /** 对存档某个值相加并存档,canNeg参数为true时,允许结果是负值,subKey为二级key */
    static addData(key: string, num: number, subKey: string = null, canNeg: boolean = false): number {
        if (num === 0) {
            return;
        }
        const orgVal = crtkc.getData(key, 0, subKey);
        let newVal = orgVal + num;
        if (!canNeg && newVal < 0) {
            newVal = 0;
        }
        crtkc.saveData(key, newVal);
        return newVal;
    }
    /** 删除存档位 */
    static delData(key: string, subKey: string = null, isSaveNow: boolean = false): void {
        if (subKey) {
            if (!crtkc.saveObj[key]) {
                return;
            }
            delete crtkc.saveObj[key][subKey];
        } else {
            delete crtkc.saveObj[key];
        }
        crtkc.isDataChange = true;
        if (!isSaveNow) {
            return;
        }
        crtkc.saveExe();
    }
    static saveData(key: string, value: any, subKey: string = null, isSaveNow: boolean = false): void {
        if (subKey) {
            if (!crtkc.saveObj[key]) {
                crtkc.saveObj[key] = {};
            }
            crtkc.saveObj[key][subKey] = value;
        } else {
            crtkc.saveObj[key] = value;
        }
        crtkc.isDataChange = true;
        if (!isSaveNow) {
            return;
        }
        crtkc.saveExe();
    }
    /** 每帧结束时执行保存,此方法建议每帧执行 */
    static saveExe() {
        if (!crtkc.isDataChange) {
            return;
        }
        localStorage.setItem(crtkc.gameName, JSON.stringify(crtkc.saveObj));
        crtkc.isDataChange = false;
        // crtkc.log('完成保存',Date.now());
    }
    /**
     * 获取存档值,支持二级key
     * @param key 一级key
     * @param defaultVal 当值为undefined或null时返回defaultVal
     * @param subKey 二级key
     * @returns 
     */
    static getData(key: string, defaultVal: any, subKey: string = null): any {
        let val = crtkc.saveObj[key];
        if (subKey) {
            if (!val) {
                return defaultVal;
            }
            val = val[subKey];
        }
        if (val === undefined || val === null) {
            return defaultVal;
        }
        return val;
    }

    static loadData(key: string): any {
        const loadData = localStorage.getItem(crtkc.gameName);
        if (loadData) {
            crtkc.saveObj = JSON.parse(loadData);
            // crtkc.log('加载本地数据成功', crtkc.saveObj);
            return crtkc.saveObj[key];
        }
        return null;
    }
    static clearData() {
        localStorage.removeItem(crtkc.gameName);
    }

    static jget(url: string, callback?: any) {
        if (!callback) {
            callback = function (err) {
                if (err) {
                    return crtkc.err(err);
                }
            };
        }
        const xhr = new XMLHttpRequest();
        xhr.responseType = 'arraybuffer';
        xhr.open('GET', url, true);
        xhr.onload = function () {
            if (xhr.status == 200) {
                // return callback(null, xhr.responseText);
                return callback(null, xhr.response);
            } else {
                return callback('[ERR-jget]:' + xhr.status);
            }
        };
        xhr.onerror = (e: any) => {
            callback(e);
        };
        xhr.send();
    }

    static jgetJson(url: string, callback?: any) {
        if (!callback) {
            callback = function (err) {
                if (err) {
                    return crtkc.err(err);
                }
            };
        }
        const xhr = new XMLHttpRequest();
        xhr.responseType = 'json';
        xhr.open('GET', url, true);
        xhr.onload = function () {
            if (xhr.status == 200) {
                // return callback(null, xhr.responseText);
                console.error("fail 200");
                return callback(null, xhr.response);
            } else {
                console.error("fail 2001");
                return callback('[ERR-jget]:' + xhr.status);
            }
        };
        xhr.onerror = (e: any) => {
            console.error("fail 2002");
            callback(e);
        };
        xhr.send();
    }


    static jpost(url: string, data: any, callback?: any) {
        if (!callback) {
            callback = function (err) {
                if (err) {
                    return crtkc.err(err);
                }
            };
        }
        const xhr = new XMLHttpRequest();
        // const now = new Date().getTime();
        xhr.open('POST', url, true);
        xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
        const postData = JSON.stringify(data);
        // geckc.log('post url:' + url);
        // geckc.log('post j:' + postData);
        xhr.send(postData);
        xhr.onerror = (e: any) => {
            callback(e);
        };
        xhr.onload = function () {
            if (xhr.status == 200) {
                if (typeof xhr.responseText == 'object') {
                    return callback(null, xhr.responseText);
                }
                let j = { 'postERR': '' + xhr.responseText };
                try {
                    j = JSON.parse(xhr.responseText);
                } catch (e) {
                    crtkc.err(e);
                    return callback(null, { 'code': '1111', 'err': 'jsonErr:' + xhr.responseText, url });
                }
                return callback(null, j);
            } else {
                return callback(null, { 'code': '' + xhr.status });
            }
        };
    };


    // static createPicNode(picName: string): cc.Node {
    //     const spf = crtResMgr.instance().getPic(picName);
    //     if (!spf) {
    //         crtkc.err('创建picNode失败:' + picName);
    //         return;
    //     }
    //     const node: cc.Node = new cc.Node();
    //     node.addComponent(cc.Sprite).spriteFrame = spf;
    //     return node;
    // }

    // private static encodeUTF8(s) {
    //     let i, c, x;
    //     const r = [];
    //     for (i = 0; i < s.length; i++) {
    //         if ((c = s.charCodeAt(i)) < 0x80) { r.push(c); } else if (c < 0x800) {
    //             r.push(0xC0 + (c >> 6 & 0x1F), 0x80 + (c & 0x3F));
    //         } else {
    //             if ((x = c ^ 0xD800) >> 10 == 0) {
    //                 c = (x << 10) + (s.charCodeAt(++i) ^ 0xDC00) + 0x10000, r.push(0xF0 + (c >> 18 & 0x7), 0x80 + (c >> 12 & 0x3F));
    //             } else {
    //                 r.push(0xE0 + (c >> 12 & 0xF));
    //             }
    //             r.push(0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
    //         }
    //     }
    //     return r;
    // }

    // private static s(s) {
    //     const data = new Uint8Array(crtkc.encodeUTF8(s));
    //     let i, j, t;
    //     const l = ((data.length + 8) >>> 6 << 4) + 16;
    //     s = new Uint8Array(l << 2);
    //     s.set(new Uint8Array(data.buffer)), s = new Uint32Array(s.buffer);
    //     for (t = new DataView(s.buffer), i = 0; i < l; i++) { s[i] = t.getUint32(i << 2); }
    //     s[data.length >> 2] |= 0x80 << (24 - (data.length & 3) * 8);
    //     s[l - 1] = data.length << 3;
    //     const w = [],
    //         f = [
    //             function () { return m[1] & m[2] | ~m[1] & m[3]; },
    //             function () { return m[1] ^ m[2] ^ m[3]; },
    //             function () { return m[1] & m[2] | m[1] & m[3] | m[2] & m[3]; },
    //             function () { return m[1] ^ m[2] ^ m[3]; }
    //         ],
    //         rol = function (n, c) { return n << c | n >>> (32 - c); },
    //         k = [1518500249, 1859775393, -1894007588, -899497514],
    //         m = [1732584193, -271733879, null, null, -1009589776];
    //     m[2] = ~m[0];
    //     m[3] = ~m[1];
    //     for (i = 0; i < s.length; i += 16) {
    //         const o = m.slice(0);
    //         for (j = 0; j < 80; j++) {
    //             w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
    //             t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0;
    //             m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
    //         }
    //         for (j = 0; j < 5; j++) { m[j] = m[j] + o[j] | 0; }
    //     }
    //     t = new DataView(new Uint32Array(m).buffer);
    //     for (i = 0; i < 5; i++) { m[i] = t.getUint32(i << 2); }

    //     const hex = Array.prototype.map.call(new Uint8Array(new Uint32Array(m).buffer), function (e) {
    //         return (e < 16 ? '0' : '') + e.toString(16);
    //     }).join('');
    //     return hex;
    // };

    // private static b(data) {
    //     let pArr = [];
    //     for (const i in data) {
    //         pArr.push(i);
    //     }
    //     pArr = pArr.sort();
    //     let srcTxt = '';
    //     for (let j = 0; j < pArr.length; j++) {
    //         let append = data[pArr[j]];
    //         if (typeof append === 'object') {
    //             append = this.b(append);
    //         }
    //         srcTxt += pArr[j] + append;
    //     }
    //     return srcTxt;
    // };


    // private static a(data, ts, k?: string) {
    //     let pArr = [];
    //     for (const i in data) {
    //         if (data[i] === null || data[i] === undefined) {
    //             delete data[i];
    //             continue;
    //         }
    //         pArr.push(i);
    //     }
    //     pArr = pArr.sort();
    //     let srcTxt = crtkc.b(data);
    //     if (ts) {
    //         srcTxt += ts;
    //     }
    //     srcTxt += (k || 'pH0Xhyenu2e1zdUg');
    //     return crtkc.s(srcTxt);
    // };

    // static jax(url, data, callback) {
    //     if (!callback) {
    //         callback = function (err) {
    //             if (err) {
    //                 return crtkc.err(err);
    //             }
    //         };
    //     }
    //     const xhr = new XMLHttpRequest();
    //     const now = Date.now();
    //     data.s = crtkc.a(data, now);
    //     xhr.open('POST', url, true);
    //     xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
    //     xhr.setRequestHeader('ts', '' + now);
    //     const postData = JSON.stringify(data);
    //     // crtkc.llog('====post:'+postData);
    //     xhr.send(postData);
    //     xhr.onload = function () {
    //         if (xhr.status == 200) {
    //             let j = { 'jaxERR': '' + xhr.responseText };
    //             try {
    //                 j = JSON.parse(xhr.responseText);
    //             } catch (e) {
    //                 crtkc.err(e);
    //                 return callback(null, { 're': '1111', 'err': 'jsonErr:' + xhr.responseText });
    //             }
    //             return callback(null, j);
    //         } else {
    //             return callback(null, { 're': '9' + xhr.status });
    //         }
    //     };
    // };


    //     static htVer: string = '203';
    //     static picType: string = '';
    //     static iconPre: string = '';
    //     static htHost: string = 'https://dk.gametdd.com/htApi/ht_api/';
    //     static htInit(gid: string, callback = (err?: any, re?: any) => { }) {
    //         if (!gid) {
    //             return;
    //         }
    //         const postData = {
    //             'gid': gid,
    //             'uid': crtkc.getData('user', {})['id'] || ('u' + Date.now()),
    //             'v': crtkc.htVer,
    //         }
    //         crtkc.jax(crtkc.htHost + 'init2', postData, (err?: any, re?: any) => {
    //             if (err || !re) {
    //                 crtkc.err('jhtERR', err);
    //                 return callback(err || new Error('no re'));
    //             }
    //             if (re.re !== 0) {
    //                 crtkc.err('jht FAILED!' + JSON.stringify(re));
    //                 return callback(new Error('err:' + re.re));
    //             }
    //             crtkc.log('HT init OK:' + crtkc.htVer);
    //             if (re.uid) {
    //                 const userObj = crtkc.getData('user', {});
    //                 userObj.id = re.uid;
    //                 crtkc.saveData('user', userObj);
    //             }
    //             if (re.picType) {
    //                 crtkc.picType = re.picType;
    //             }
    //             crtkc.iconPre = re.iconPre;
    //             // console.log('====> 互推re:',JSON.stringify(re));
    //             callback(null, re);
    //         });
    //     }

    //     static htGetBox(bid: string, callback = (err?: any, re?: any) => { }): void {
    //         if (!bid) {
    //             crtkc.err('htGetBox bid为空!!');
    //             return;
    //         }
    //         const postObj = {
    //             'bid': bid,
    //         };

    //         crtkc.jax(crtkc.htHost + 'getBox', postObj, (err: any, re?: any) => {
    //             if (err) {
    //                 return callback(err);
    //             }
    //             if (re.re !== 0 || !re.arr) {
    //                 return callback(new Error('服务配置有误bid:' + bid + ',re:' + re.re));
    //             }

    //             //  console.log('====> re:',JSON.stringify(re));


    //             /*
    // { re: 0,
    //   arr:
    //    [ [ '5dce6f748057f44580514c48@oppoPgkname@2' ],
    //      [ '5dcbccf46fb82c3491fde34b@wxtestkey2@0' ],
    //      [ '5dce6f748057f44580514c48@oppoPgkname@0' ],
    //      [ '5dcbccf46fb82c3491fde34b@wxtestkey2@0' ],
    //      [ '5dce6f748057f44580514c48@oppoPgkname@0' ],
    //      [ '5dcbccf46fb82c3491fde34b@wxtestkey2@0' ] ] }

    //             */
    //             const mArr = [];
    //             const iconMap = {};
    //             for (let i = 0; i < re.arr.length; i++) {
    //                 const oneA = re.arr[i];
    //                 for (let j = 0; j < oneA.length; j++) {
    //                     const msArr = oneA[j].split('@');
    //                     let ver = '';
    //                     let mark = '';
    //                     if (msArr.length >= 4 && msArr[3] !== '0') {
    //                         ver = '_' + msArr[3];
    //                     }
    //                     if (msArr.length >= 5) {
    //                         mark = msArr[4];
    //                     }
    //                     // mSubArr.push({ 'jumpKey': msArr[1], 'mid': msArr[0], 'iconUrl': HtApi.IconPre + msArr[0] + ver + '.' + this.picType, 'txt': msArr[2], 'mark': mark, 'query': 'mid=' + msArr[0] + '&bid=' + bid + '&uid=' + HtApi.Uid + '&po=' + i });
    //                     if (!iconMap[msArr[0]]) {
    //                         mArr.push({ 'jumpKey': msArr[1], 'mid': msArr[0], 'iconUrl': crtkc.iconPre + msArr[0] + ver + '.' + crtkc.picType, 'txt': msArr[2], 'mark': mark });
    //                         iconMap[msArr[0]] = msArr[0];
    //                     }
    //                 }
    //             }
    //             callback(null, mArr);
    //         });
    //     }



    //     /**
    //     * 抖动目标节点
    //     * @param node 目标节点
    //     * @param durating 动画持续时间
    //     * @param ang 转动角度
    //     * @param times 重复次数
    //     * @param isforever 是否永远动画（true：times无效）
    //     */
    //     public static ani_JitterNode(node: cc.Node, duration: number, ang: number, times: number, isforever: boolean = false) {
    //         if (isforever) {
    //             let ani1 = cc.tween(node).by(duration / 4, { angle: ang }).by(duration / 2, { angle: -ang * 2 }).by(duration / 4, { angle: ang });
    //             let ani2 = ani1.delay(0.5);
    //             cc.tween(node).repeatForever(ani2).start();
    //         } else {
    //             let ani1 = cc.tween(node).by(duration / 2, { angle: ang }).by(duration / 2, { angle: -ang })
    //             let ani2 = cc.tween(node).sequence(ani1, cc.tween(node).delay(0.1), ani1.reverseTime()).delay(0.1);
    //             ani2.repeat(times).start();
    //         }
    //     }



    //     static getDaysDifference(timestamp1: number, timestamp2: number): number {
    //         const oneDay = 24 * 60 * 60 * 1000; // 一天的毫秒数
    //         const date1 = new Date(timestamp1);
    //         const date2 = new Date(timestamp2);

    //         // 计算两个日期之间的毫秒差
    //         const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime());


    //         // 将毫秒差转换为天数
    //         const diffInDays = Math.floor(diffInMilliseconds / oneDay);

    //         return diffInDays;
    //     }
    static eventReport(eventName: string, eventProp?: string, userProps?: any): void {
        //web为测试，不上报数据
        console.error("gid", crtChannel.getChannel().getPara('gid'));
        if (!crtChannel.getChannel().getPara('gid')) {
            return;
        }
        let userId: string = this.getData('userId', null)
        if (userId === null) {
            userId = this.randomStr(16)
            this.saveData('userId', userId)
        }
        //支付宝的post需要特殊处理 
        crtChannel.jpost('https://dk.gametdd.com/htApi/ht_api/eventReport', { 'eventName': eventName, 'eventProp': eventProp, 'gid': crtChannel.getChannel().getPara('gid'), 'uid': userId, 'userProps': userProps }, (err, re) => {
            if (err) {
                crtkc.err(err);
                return;
            }
            console.error("上报数据", JSON.stringify(re));
        });
    }


}
